package com.jnetx.javax.sip.message;

import com.jnetx.javax.sip.header.*;
import com.jnetx.javax.sip.util.ByteArrayUtil;
import com.jnetx.javax.sip.util.ByteData;
import com.jnetx.javax.sip.util.CharacterTree;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.sip.header.*;
import java.text.ParseException;

/**
 * Created by IntelliJ IDEA.
 * User: deps
 * Date: 28.03.11
 * Time: 21:39
 * To change this template use File | Settings | File Templates.
 */
public class SIPMessageParser {
    private final static Log log = LogFactory.getLog(SIPMessageParser.class);

    private static final CharacterTree<HeaderParser> headerParsers = new CharacterTree<HeaderParser>();

    static {
        headerParsers.put(AcceptEncodingHeader.NAME, new AcceptEncodingHeaderParser());
        headerParsers.put(AcceptHeader.NAME, new AcceptHeaderParser());
        headerParsers.put(AcceptLanguageHeader.NAME, new AcceptLanguageHeaderParser());
        headerParsers.put(AlertInfoHeader.NAME, new AlertInfoHeaderParser());
        headerParsers.put(AllowEventsHeader.NAME, new AllowEventsHeaderParser());
        headerParsers.put(AllowHeader.NAME, new AllowHeaderParser());
        headerParsers.put(AuthenticationInfoHeader.NAME, new AuthenticationInfoHeaderParser());
        headerParsers.put(AuthorizationHeader.NAME, new AuthorizationHeaderParser());
        headerParsers.put(CallIdHeader.NAME, new CallIdHeaderParser());
        headerParsers.put(CallInfoHeader.NAME, new CallInfoHeaderParser());
        headerParsers.put(ContactHeader.NAME, new ContactHeaderParser());
        headerParsers.put(ContentDispositionHeader.NAME, new ContentDispositionHeaderParser());
        headerParsers.put(ContentEncodingHeader.NAME, new ContentEncodingHeaderParser());
        headerParsers.put(ContentLanguageHeader.NAME, new ContentLanguageHeaderParser());
        headerParsers.put(ContentLengthHeader.NAME, new ContentLengthHeaderParser());
        headerParsers.put(ContentTypeHeader.NAME, new ContentTypeHeaderParser());
        headerParsers.put(CSeqHeader.NAME, new CSeqHeaderParser());
        headerParsers.put(DateHeader.NAME, new DateHeaderParser());
        headerParsers.put(ErrorInfoHeader.NAME, new ErrorInfoHeaderParser());
        headerParsers.put(EventHeader.NAME, new EventHeaderParser());
        headerParsers.put(ExpiresHeader.NAME, new ExpiresHeaderParser());
        headerParsers.put(FromHeader.NAME, new FromHeaderParser());
        headerParsers.put(InReplyToHeader.NAME, new InReplyToHeaderParser());
        headerParsers.put(MaxForwardsHeader.NAME, new MaxForwardsHeaderParser());
        headerParsers.put(MimeVersionHeader.NAME, new MimeVersionHeaderParser());
        headerParsers.put(MinExpiresHeader.NAME, new MinExpiresHeaderParser());
        headerParsers.put(OrganizationHeader.NAME, new OrganizationHeaderParser());
        headerParsers.put(PriorityHeader.NAME, new PriorityHeaderParser());
        headerParsers.put(ProxyAuthenticateHeader.NAME, new ProxyAuthenticateHeaderParser());
        headerParsers.put(ProxyAuthorizationHeader.NAME, new ProxyAuthorizationHeaderParser());
        headerParsers.put(ProxyRequireHeader.NAME, new ProxyRequireHeaderParser());
        headerParsers.put(RAckHeader.NAME, new RAckHeaderParser());
        headerParsers.put(ReasonHeader.NAME, new ReasonHeaderParser());
        headerParsers.put(RecordRouteHeader.NAME, new RecordRouteHeaderParser());
        headerParsers.put(ReferToHeader.NAME, new ReferToHeaderParser());
        headerParsers.put(ReplyToHeader.NAME, new ReplyToHeaderParser());
        headerParsers.put(RequireHeader.NAME, new RequireHeaderParser());
        headerParsers.put(RetryAfterHeader.NAME, new RetryAfterHeaderParser());
        headerParsers.put(RouteHeader.NAME, new RouteHeaderParser());
        headerParsers.put(RSeqHeader.NAME, new RSeqHeaderParser());
        headerParsers.put(ServerHeader.NAME, new ServerHeaderParser());
        headerParsers.put(SubjectHeader.NAME, new SubjectHeaderParser());
        headerParsers.put(SubscriptionStateHeader.NAME, new SubscriptionStateHeaderParser());
        headerParsers.put(SupportedHeader.NAME, new SupportedHeaderParser());
        headerParsers.put(TimeStampHeader.NAME, new TimeStampHeaderParser());
        headerParsers.put(ToHeader.NAME, new ToHeaderParser());
        headerParsers.put(ViaHeader.NAME, new ViaHeaderParser());
    }

    public static SIPMessage parse(byte[] messageData) {
        SIPMessage result = null;
        final int firstSpace = ByteArrayUtil.firstOccurence(messageData, ' ');
        try {
            for (int i=1; i<4; i++) {
                int index = firstSpace +i;
                if (index < messageData.length) {
                    if (messageData[index]<'0' || messageData[index]>'9')
                        return SIPRequestParser.parseRequest(messageData);
                } else throw new ParseException("Malformed SIP message!", index);
            }
            if (messageData[firstSpace+4]!=' ')
                throw new ParseException("Malformed SIP message!", firstSpace+4);
            result = SIPResponseParser.parseResponse(messageData);
        }catch (ParseException e){
            log.error("Cannot parse the SIP message!", e);
        }
        return result;
    }

    static int parseHeaders(byte[] requestData, SIPMessage message, int startOfHeaders) throws ParseException {
        int startOfHeader = startOfHeaders;
        while (true) {
            final int endOfHeader = ByteArrayUtil.firstEndOfLine(requestData, startOfHeader);
            if (endOfHeader == -1)
                fail("Cannot determine the end of line for the header!", startOfHeader);
            if (endOfHeader == startOfHeader) { //empty line, that means end of header section
                startOfHeader += 2;
                break;
            }
            //doParse the header name
            final int endOfHeaderName = ByteArrayUtil.firstOccurence(requestData, ':', startOfHeader);
            if (endOfHeaderName == -1)
                fail("Cannot determine the header name!", startOfHeader);
            final String headerName = new String(requestData, startOfHeader, endOfHeaderName - startOfHeader);
            HeaderParser parser = headerParsers.get(headerName);
            if (parser == null) {
                //Unrecognized header
                message.addUnrecognizedHeader(new ByteData(requestData, startOfHeader, endOfHeader));
            } else {
                //Well-known header
                final int startOfHeaderValue = ByteArrayUtil.firstMissing(requestData, ' ', endOfHeaderName + 1, endOfHeader);
                Header header = parser.createLazily(requestData, startOfHeaderValue == -1 ? (endOfHeaderName + 1) : startOfHeaderValue, endOfHeader);
                message.addHeader(header);
            }
            startOfHeader = endOfHeader + 2;
        }
        return startOfHeader;
    }


    static void fail(String message, int offset) throws ParseException {
        log.error(message);
        throw new ParseException(message, offset);
    }
}
